import numpy as np  # 导入numpy库，用于数据处理
import torch  # 导入torch库，用于构建和训练神经网络
import matplotlib.pyplot as plt  # 导入matplotlib.pyplot库，用于数据可视化
# Pregnancies,Glucose,BloodPressure,SkinThickness,Insulin,BMI,DiabetesPedigreeFunction,Age,Outcome
# 准备数据集
xy = np.loadtxt('diabetes.csv', delimiter=',', dtype=np.float32)  # 从CSV文件中加载数据，指定分隔符为逗号，数据类型为float32
x_data = torch.from_numpy(xy[:, :-1])  # 将数据集中的特征部分（所有行，除了最后一列）转换为torch张量
# 第一个‘:’表示选取所有行，第二个‘:-1’表示从第一列到倒数第二列（Python索引中-1表示最后一列）
y_data = torch.from_numpy(xy[:, [-1]])  # 将数据集中的标签部分（所有行的最后一列）转换为torch张量
# [-1]表示选取最后一列，但这样得到的是一个二维数组（矩阵），实际上在这个场景中，我们可能希望它是一个一维张量
# 不过，由于后续操作并未特别要求y_data的维度，且BCELoss可以接受二维输入（其中第二维大小为1），因此这里不会引发问题

# 使用类设计模型
class Model(torch.nn.Module):  # 定义一个继承自torch.nn.Module的类，用于构建神经网络模型
    def __init__(self):
        super(Model, self).__init__()  # 调用父类的构造函数
        self.linear1 = torch.nn.Linear(8, 6)  # 定义第一个线性层，输入特征维度为8，输出特征维度为6
        self.linear2 = torch.nn.Linear(6, 4)  # 定义第二个线性层，输入特征维度为6，输出特征维度为4
        self.linear3 = torch.nn.Linear(4, 1)  # 定义第三个线性层，输入特征维度为4，输出特征维度为1（对应一个预测值）
        self.sigmoid = torch.nn.Sigmoid()  # 定义一个Sigmoid激活函数层，用于将输出映射到(0,1)区间

    def forward(self, x):  # 定义前向传播过程
        x = self.sigmoid(self.linear1(x))  # 输入x通过第一个线性层后，应用Sigmoid激活函数
        x = self.sigmoid(self.linear2(x))  # 经过激活的输出作为第二个线性层的输入，再次应用Sigmoid激活函数
        x = self.sigmoid(self.linear3(x))  # 经过第二个激活的输出作为第三个线性层的输入，最后应用Sigmoid激活函数得到预测值y_hat
        return x

model = Model()  # 实例化模型

# 构造损失函数和优化器
# criterion = torch.nn.BCELoss(size_average = True)  # 这行代码被注释掉了，size_average参数在新版本中已被重命名为reduction
criterion = torch.nn.BCELoss(reduction='mean')  # 使用二分类交叉熵损失函数，reduction='mean'表示对损失进行平均
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)  # 使用随机梯度下降优化器，学习率设置为0.1

epoch_list = []  # 初始化一个空列表，用于存储每个epoch的索引
loss_list = []  # 初始化一个空列表，用于存储每个epoch的损失值

# 训练循环：前向传播，反向传播，参数更新
for epoch in range(100):  # 训练100个epoch
    y_pred = model(x_data)  # 前向传播，得到预测值
    loss = criterion(y_pred, y_data)  # 计算损失值，注意y_data虽然是一个二维张量，但BCELoss可以处理这种情况
    print(epoch, loss.item())  # 打印当前epoch和损失值
    epoch_list.append(epoch)  # 将当前epoch索引添加到epoch_list中
    loss_list.append(loss.item())  # 将当前损失值添加到loss_list中

    optimizer.zero_grad()  # 清零梯度
    loss.backward()  # 反向传播，计算梯度

    optimizer.step()  # 更新模型参数

# 数据可视化：绘制损失随epoch变化的曲线
plt.plot(epoch_list, loss_list)  # 使用plt.plot函数绘制曲线
plt.ylabel('loss')  # 设置y轴标签为'loss'
plt.xlabel('epoch')  # 设置x轴标签为'epoch'
plt.show()  # 显示图表